package ad_exercise_2.model;

import java.util.ArrayList;
import java.util.Arrays;

/**
 * Deze klasse bestaat uit een matrix, waarin het stationnetwerk is weergegeven. De rijen en kolommen weerspiegelen alle stations. De waarden in de matrix staan voor de afstand van de verbinding tussen desbetreffende stations. Als er een
 * waarde, gelijk aan de INF-waarde staat, betekent dat, dat er geen directe verbinding tussen de stations bestaat.
 * 
 * @author Karen Berntsen (s0815799)
 * @author Erik Stens (s0615374)
 * 
 */
public class StationNetwerk_matrix extends StationNetwerk {
	
	private int[][]		afstanden;
	private int[]		kortsteAfstand;
	private boolean[]	coloured;
	private int[]		route;
	
	public StationNetwerk_matrix() {
		super();
	}
	
	public StationNetwerk_matrix(String filename) {
		super(filename);
	}
	
	/**
	 * initializes the matrix and fills it with 'infinite' values
	 * 
	 * @param numStations
	 *            number of stations that have to fit in the matrix
	 */
	@Override
	public void init(int numStations) {
		super.init(numStations);
		
		afstanden = new int[numStations][numStations];
		for (int[] stationArr : afstanden) {
			Arrays.fill(stationArr, INF);
		}
		
		kortsteAfstand = new int[numStations];
		Arrays.fill(kortsteAfstand, INF);
		coloured = new boolean[numStations];
		route = new int[numStations];
		Arrays.fill(route, -1);
		Arrays.fill(coloured, false);
	}
	
	public String getStationNaam(int station) {
		return Integer.toString(station + 1);
	}
	
	public int getAfstand(int station1, int station2) {
		if (!inBounds(station1) || !inBounds(station2))
			throw new RuntimeException("Opgegeven station bestaat niet!");
		
		return afstanden[station1][station2];
	}
	
	@Override
	public void addVerbinding(int station1, int station2, int afstand) {
		if (!inBounds(station1) || !inBounds(station2))
			throw new RuntimeException("Opgegeven station past niet in het netwerk! Verbinding niet toegevoegd!");
		
		setAfstand(station1, station2, afstand);
	}
	
	/**
	 * Zet opgegeven afstand in de afstandsmatrix. Verbindingen tussen stations zijn symmetrisch!
	 * 
	 * @param station1
	 * @param station2
	 * @param afstand
	 */
	public void setAfstand(int station1, int station2, int afstand) {
		if (!inBounds(station1) || !inBounds(station2))
			throw new RuntimeException("Opgegeven station bestaat niet!");
		
		afstanden[station1][station2] = afstand;
		afstanden[station2][station1] = afstand;
	}
	
	public int[][] getAfstanden() {
		return afstanden;
	}
	
	public void setAfstanden(int[][] afstanden) {
		this.afstanden = afstanden;
	}
	
	public void setColour(int stationIndex) {
		coloured[stationIndex] = true;
	}
	
	public boolean getColour(int stationIndex) {
		return coloured[stationIndex];
	}
	
	public void setRoute(int station, int afstand) {
		if (!inBounds(station))
			throw new RuntimeException("Opgegeven station bestaat niet!");
		
		kortsteAfstand[station] = afstand;
	}
	
	/**
	 * Deze methode maakt een ArrayList<Integer> aan, waarin alle indices van de stations die gepasseerd worden op de route in chronologische volgorde staan. Hierbij wordt gebruik gemaakt van recursie.
	 * 
	 * @param firstStation
	 * @param lastStation
	 * 
	 * @return de route als een ArrayList<Integer> van station-indices
	 */
	public ArrayList<Integer> getRoute(int firstStation, int lastStation) {
		ArrayList<Integer> finalRoute = new ArrayList<Integer>();
		finalRoute.add(lastStation);
		finalRoute = getRoute(firstStation, lastStation, finalRoute);
		return finalRoute;
	}
	
	private ArrayList<Integer> getRoute(int firstStation, int lastStation, ArrayList<Integer> finalRoute) {
		int stationInd = route[lastStation];
		if (stationInd != firstStation) {
			finalRoute.add(0, stationInd);
			finalRoute = getRoute(firstStation, stationInd, finalRoute);
		} else {
			finalRoute.add(0, firstStation);
		}
		return finalRoute;
	}
	
	public void printRoute(ArrayList<Integer> finalRoute) {
		System.out.println("The shortest route is:");
		for (int i : finalRoute) {
			System.out.print(i);
			System.out.print(",");
		}
	}
	
	/**
	 * Berekent de kortste route tussen twee stations.
	 * 
	 */
	@Override
	public void kortsteRoute(int beginStationIndex, int eindStationIndex) {
		setRoute(beginStationIndex, 0);
		route[0] = 0;
		setColour(beginStationIndex);
		int lastColoured = beginStationIndex;
		kortsteAfstand[lastColoured] = 0;
		int routeAfstand = 0;
		while (!getColour(eindStationIndex)) {
			for (int i = 0; i < numStations; i++) {
				if (afstanden[lastColoured][i] + routeAfstand < kortsteAfstand[i]) {
					kortsteAfstand[i] = afstanden[lastColoured][i] + routeAfstand;
					route[i] = lastColoured;
				}
			}
			int minimum = INF;
			int index = beginStationIndex;
			for (int i = 0; i < numStations; i++) {
				if (!coloured[i] && kortsteAfstand[i] < minimum) {
					minimum = kortsteAfstand[i];
					index = i;
				}
			}
			lastColoured = index;
			setColour(index);
			routeAfstand = minimum;
		}
		System.out.print("min afstand = ");
		System.out.println(routeAfstand);
		ArrayList<Integer> finalRoute = getRoute(beginStationIndex, eindStationIndex);
		printRoute(finalRoute);
	}
}